react函数式组件和类组件创建ref的方法

react函数式组件和类组件创建ref的方法

类组件创建ref

父组件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 引入React
import React, { Component } from 'react';
import EditableTable from './EditableTable';
export default class Content extends Component {
constructor(props) {
super(props);
// 通过React.createRef()创建ref,挂载到父组件上
this.editTableEl = React.createRef();
}
render() {
return (
<div>
<EditableTable
// 挂载ref
ref={this.editTableEl}
/>
<button onClick={()=>{alert(this.editTableEl.current.os())}}>父组件的button</button>
</div>
)
}
}

子组件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 引入React
import React, { Component } from 'react';
export default class EditableTable extends Component {
constructor(props) {
super(props);
this.state={
inp:""
}
// 通过React.createRef()创建ref,挂载到组件上
this.editTableEls = React.createRef();
}
componentDidMount(){
this.editTableEls.current.os=this.os
}
os=()=>this.state.inp
render() {
return (
<div>子组件的输入框
<input type="text" ref={this.editTableEls} value={this.state.inp} onChange={(e)=>this.setState({inp:e.target.value})}/>
</div>
)
}
}

函数式组件创建ref

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import React, { useRef, useImperativeHandle } from 'react';
import ReactDOM from 'react-dom';

//父组件
const App = props => {
const fancyInputRef = useRef();
// 创建ref相当于React.createRef()
return (
<div>
<FancyInput ref={fancyInputRef} />
{/* 挂载ref */}
<button
onClick={() => fancyInputRef.current.focus()}
>父组件调用子组件的 focus</button>
</div>
)
}
// 子组件
const FancyInput = React.forwardRef((props, ref) => {
const inputRef = useRef();
useImperativeHandle(ref, () => ({
focus: () => {
inputRef.current.focus();
}
}));
// useImperativeHandle()第一个参数是cueent对象的ref,第二个是个函数,返回一个对象,第三个参数,只有参数里值发生变化父组件接收的值才会变化才会引发变化

return <input ref={inputRef} type="text" />
});

export default App

谷歌v8引擎学习

谷歌v8引擎学习

谷歌v8引擎学习

1.v8是如何执行一段代码的?

解释执行与编译执行

  • 解释执行:输入代码——解析器——中间代码——解释器——输出结果

Alt text

  • 编译执行:输入代码——解析器——中间代码——编译器——机器代码——输出结果

Alt text

  • 优缺点:解释执行的启动速度快,但是执行时的速度慢,而编译执行的启动速度慢,但是执行时的速度快

  • v8采用了,解释执行与编译执行相结合混合编译执行的技术也就是JIT(Just In Time)

v8执行图解

  1. 初始化执行环境:事件循环系统,全局作用域,全局执行上下文,堆和栈,消息循环系统

  2. 输入一行代码

  3. 解析器解析:v8会结构化这段字符串,生成抽象语发树(AST)和相关作用域

  4. 中间代码字节码:字节码是介于AST机器代码的中间代码,字节码可以被解释器直接执行,或者编译器编译成二进制机器代码执行。

  5. 解释器:监听器判断是不是热点代码(代码多次被执行),如不是直接有解释器输出结构(这一步也就是解释执行的实际应用

  6. 编译器(编译热点代码)优化:将热点代码编译成机器码,这样下次执行是就可以忽略前面过程直接输出结果,这是编译执行的过程。

  7. 编译器反优化:如果对象的结构和属性发生改变,那么v8就会调用反优化,将二级制代码回退到优化之前,下次执行就会回退到解释器执行了。
    Alt text

2.v8中函数的特性与函数自身的特性

v8赋予函数的2个隐藏属性:

name的默认值是anonymous,他表示函数对象没有被设置名称;
b表示函数代码,以字符串存储在内存中,当函数执行时,v8会从函数中抽离出code属性值,解释执行这段代码

函数是一等公民

如果某个编程语言的函数,可以和这个语言的数据类型做一样的事情,我们就把和这个语言中的函数称为一等公民

3.v8是怎样提升对象属性访问速度的?

常规属性与排序属性

  1. 常规属性:字符串属性根据创建时的顺序升序排列,字符串属性就被称为常规属性,在V8中被称为properties

  2. 排序属性:在EMAScript规范中定义了数字属性应该按照索引值大小升序排列,在这里我们把对象中的数字属性称为排序属性

v8专属快属性,慢属性和对象内属性

  1. v8会给对象增加2个隐藏属性,elements(存放排序属性),properties(按照创建时的顺序保存了常规属性),把对象处理成2种线性数据结构
    Alt text

2.对象内属性:properties存储了常规属性,这样简化了复杂度,但是却多了一步操作,查找B时需要先找properties,再找B,这样影响效率,于是v8采用了权衡的策略以加快查找属性的的效率,把部分属性存储到对象本身,这种叫做对象内属性(in-object properties),默认最多为10个,超过放到properties上
Alt text

3.快属性与慢属性

  • 快属性:只需要通过索引即可访问的属性叫做快属性。访问速度快,但是大量删除或添加属性时,执行速度非常低,耗费大量时间和内存。

  • 慢属性:对象的属性过多时,V8就会采取另外一种存储策略,那就是“慢属性”策略。慢属性的对象内部会有独立的非线性数据结构(词典)作为属性存储容器。
    Alt text

提升对象属性访问速度总结

  • 为了提升查找效率,v8在对象中添加了2个隐藏属性,排序属性和常规属性;element指向element对象(按照顺序存放排序属性),properties指向properties对象(按照创建顺序存放常规属性)

4.v8如何实现闭包的?

惰性解析扛起大旗

何谓惰性解析:解析器在解析的过程中,如遇到函数声明,name会跳过函数内部的代码,并不会为其生成AST码和字节码,而仅仅生成顶层代码的AST码和字节码。可以加快代码解析速度,极大地减少用户等待时间

预解析器补齐短板

惰性解析短板:虽然采取了惰性解析,但是仅仅解析顶层代码,如果函数内部还有函数那样就会一起销毁了,显然是不对的。于是谷歌采用了预解析器,他会对函数内部做一次快速地预解析,一是为了判断当前函数是不是存在语法上的错误,二是检查函数内部是不是引用了外部变量,如引用,v8会将栈变量复制到堆中,下次直接调用堆中的

5.v8核心垃圾回收机制

垃圾回收代际假说(The Generational Hypothesis)

代际假说(The Generational Hypothesis)的两大特点

  1. 大部分对象都是朝生暮死,比如函数内部变量,块级作用域中的变量等,代码执行完以后就会销毁,这类对象分配的内存很快就会变得不可访问。

  2. 第二个是不死的对象,会活得更久,比如全局的window、DOM、Web API等对象。

新生代垃圾回收-Minor GC(副垃圾回收器)

  1. 小的对象会被分配到新生代

  2. 这个区域比较小,新生代垃圾回收频繁,需要将活的对象复制到空闲区域,提高执行效率,所以分配了小的空间。

  3. 新生代区域分为:对象区和空闲区。数据最开始存储在对象区

  4. 新生代垃圾回收步骤:内存满了触发垃圾回收——>可访问性(reachability算法进行标记——>将存活的对象复制到空闲区(有序的排列)——>对象区清空——>然后空闲区与对象区翻转——>完成新生代垃圾回收

  5. 对象晋升策略副垃圾回收器还会采用对象晋升策略,也就是移动那些经过两次垃圾回收依然还存活的对象到老生代中。

老生代垃圾回收-Major GC(主垃圾回收器)

  1. 大的对象,新生代经过两次垃圾回收依然存在的对象

  2. 步骤:标记——>标记-清除算法——>标记-整理算法——>完成

  3. 标记阶段:从一组根元素开始,遍历递归这组元素,能访问的元素成为活动对象,否则就是垃圾数据

  4. 清除阶段:他与副垃圾回收器不一样,他仅仅将垃圾数据清楚,并未对内存碎片进行整理。这也就导致了碎片过多,无法分配足够的连续内存

  5. 整理阶段:先标记回收对象,让所有存活对象向一端移动,并清理掉这一端之外的内存

6.V8是如何优化垃圾回收器执行效率的?

全停顿(Stop-The-World)

JavaScript是执行再主线程上的(单线程),垃圾回收一旦执行就会阻塞js,带到垃圾回收完毕,再恢复js进程,我们把这种行为叫做全停顿(Stop-The-World)

v8垃圾回收特性能提升做了哪些?

如果回收过程超过200毫秒用户就会觉得页面卡顿,于是:

  1. 将一个完整的垃圾回收任务拆分成多个小的任务(解决长任务)

  2. 将标记对象,移动对象等任务转移到后台线程进行(减少主线程暂停时间)

并行回收(优化副垃圾回收器)

  1. 在执行一个完整的垃圾回收过程中,垃圾回收器会使用多个辅助线程来并行执行垃圾回收。

  2. 垃圾回收所消耗的时间,等于总体辅助线程所消耗的时间(辅助线程数量乘以单个线程所消耗的时间),再加上一些同步开销的时间。

  3. 仍然是一种全停顿的垃圾回收方式

增量回收(优化主垃圾回收器)

增量标记的算法,比全停顿的算法要稍微复杂,这主要是因为增量回收是并发的(concurrent),要实现增量执行,需要满足两点要求:

  1. 垃圾回收可以被随时暂停和重启,暂停时需要保存当时的扫描结果,等下一波垃圾回收来了之后,才能继续启动。

  2. 在暂停期间,被标记好的垃圾数据如果被JavaScript代码修改了,那么垃圾回收器需要能够正确地处理。

  3. 三色标记法写屏障机制可以很好地实现增量垃圾回收。

并发(concurrent)回收

并发回收的优势非常明显,主线程不会被挂起,JavaScript 可以自由地执行 ,在执行的同时,辅助线程可以执行垃圾回收操作。

主垃圾回收器同时采用了这三种策略

  • 首先主垃圾回收器主要使用并发标记,我们可以看到,在主线程执行JavaScript,辅助线程就开始执行标记操作了,所以说标记是在辅助线程中完成的

  • 标记完成之后,再执行并行清理操作。主线程在执行清理操作时,多个辅助线程也在执行清理操作

  • 另外,主垃圾回收器还采用了增量标记的方式,清理的任务会穿插在各种JavaScript任务之间执行

Alt text

react 内存泄露常见问题解决方案

react 内存泄露常见问题解决方案

报错分析

1
Cant perform a React state update on an unmounted component. This is a no-op, but it indicates a memory leak in your application. To fix, cancel all subscriptions and asynchronous tasks in the componentWillUnmount method

什么是内存泄露?

  • 不再用到的内存,没有及时释放,就叫做内存泄漏(memory leak)。

    几种常见的内存泄漏

  • 全局变量引起的内存泄漏
  • 闭包引起的内存泄漏
  • dom清空或删除时,事件未清除导致的内存泄漏

    例如

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

componentWillMount: function () {
var onLogin = this.props.onLogin || function () {},
onLogout = this.props.onLogout || function () {};

this.on('authChange', function () {
console.log('user authenticated:', this.state.isAuthenticated);
return this.state.isAuthenticated
? onLogin(this.state)
: onLogout(this.state);
}.bind(this));
},
很明显这种情况就是在 dom 结构销毁的时候,事件却没有清除导致的内存泄漏,所以我们需要在componentWillUnmount的时候去清除挂载的方法//
componentWillUnmount: function () {
this.off('authChange', this.authChange);
this.authChange = null;
}

条形码的插件生成

React 生成二维码/条形码的插件

####安装

npm install qrcode.react –save

使用

import QRCode from ‘qrcode.react’;

1
2
3
4
React.render(
<QRCode value="https://jian521ymn.github.io/" />,
mountNode
);

属性及其用法

prop type default value
value string
renderAs string (‘canvas’ ‘svg’) ‘canvas’
size number 128
bgColor string (css color) “#FFFFFF”
fgColor string (css color) “#000000”
level string (‘L’ ‘M’ ‘Q’ ‘H’) ‘L’
includeMargin boolean false

利用canves画布实现获取指定区域的颜色值

利用canves画布实现获取指定区域的颜色值

实现原理

  • 利用文件选择器获得图片文件base64数据
  • 利用canves对图片进行重绘
  • 截取canves相对应区域内容
  • 读取该色素点颜色范围
  • 输出
    ####代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
<!DOCTYPE HTML PUBLIC>
<html>

<head>
<meta charset="utf-8">
<title>获取图片像素颜色,利用cancves实现</title>

<style type="text/css">
body {
margin: 0px;
background: #f2f2f0;
}

p {
margin: 0px;
}

.title {
color: #FFFF00;
background: #000000;
text-align: center;
font-size: 24px;
line-height: 50px;
font-weight: bold;
}

.file {
position: absolute;
width: 100%;
font-size: 90px;
}

.filebtn {
display: block;
position: relative;
height: 110px;
color: #FFFFFF;
background: #06980e;
font-size: 48px;
line-height: 110px;
text-align: center;
cursor: pointer;
border: 3px solid #cccccc;
}

.filebtn:hover {
background: #04bc0d;
}

.showimg {
margin: 10px auto 10px auto;
-webkit-transition: all .3s;
transition: all .3s;
}

.showimg span {
width: 1px;
height: 1px;
display: block;
margin: -1px 0px 0px -1px;
}

.css_code {
margin: 10px;
padding: 10px;
display: none;
border: 1px solid #FFCC00;
font-size: 12px;
background: #F1F1F1;
white-space: pre-wrap;
word-wrap: break-word;
height: 300px;
overflow: auto;
}

.css_code:hover {
background: #DEFEDE;
}

#box img {
width: 100px;
margin: 20px;
}
</style>



</head>

<body>
<p class="title">获取图片像素颜色,转换</p>
<p><input type="file" class="file" id="img" multiple="multiple"><label class="filebtn" for="img"
title="JPG,GIF,PNG">请选择图片</label>
</p>
<p class="showimg" id="showimg"><span></span></p>
<p class="css_code" id="css_code"></p>
<div id="box">

</div>
<script type="text/javascript">
let ary = [],
ind = 0,
str = ``
window.onload = function () {

var canvas = document.createElement('canvas');
var context = canvas.getContext('2d');
var showimg = document.getElementById('showimg');
var shadow = showimg.getElementsByTagName('span')[0];
var css_code = document.getElementById('css_code');

document.getElementById('img').onchange = function () {
let eve = event.target
let timer = setInterval(() => {

var img = eve.files[ind];

// 检查能否读取图片
if (!img) {
return;
}

// // 检查图片类型
// if (!(img.type.indexOf('image') == 0 && img.type && /\.(?:jpg|png|gif)$/.test(img.name))) {
// alert('图片只能是jpg,gif,png');
// return;
// }

// 检查图片尺寸
if (img.size > 1024 * 1024) {
alert('图片不能大于120K');
return;
}
// file reader
var reader = new FileReader();
reader.readAsDataURL(img);
reader.onload = function (e) { // reader onload start

var image = new Image();
image.src = e.target.result;

image.onload = function () { // image onload start

var img_width = this.width;
var img_height = this.height;

// 设置画布尺寸
canvas.width = img_width;
canvas.height = img_height;
// 将图片按像素写入画布
context.drawImage(this, 0, 0, img_width, img_height);
// document.getElementById("box").appendChild(canvas)
//显示图片
document.getElementById("box").appendChild(image)
// 读取图片像素信息
var imageData = context.getImageData(0, 0, 7, 7);
let len = imageData.data.length / 4
let r = 0,
g = 0,
b = 0,
num = [];
for (let i = 0; i < imageData.data.length; i += 4) {
r += imageData.data[i];
g += imageData.data[i + 1];
b += imageData.data[i + 2];

}
// console.log(imageData.data,);
num = [r / len, g / len, b / len]
let newrgb = [Math.floor(num[0] / 9), Math.floor(num[1] / 9), Math.floor(num[2] / 9)]
yurgb = [Math.floor((num[0] % 9) / 6), Math.floor((num[1] % 9) / 6), Math.floor((num[2] % 9) /
6)]
let allrgb = [(yurgb[0] + newrgb[0]) * 9, (yurgb[1] + newrgb[1]) * 9, (yurgb[2] + newrgb[2]) *
9
]
// console.log('img', num, newrgb, yurgb, allrgb);
ary.push(allrgb)
}
}
ind++
if (ind === eve.files.length) {
let inn = document.getElementById("box");
clearInterval(timer)
}

}, 1000);



}
}
</script>
</body>

</html>

promise的all,race,finally源码解析

promise的all,race,finally源码解析

promise.all源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
//传入为空返回成成功态,所有加载完才会,改成功态,一个失败态就会直接结束
Promise.all_ = function (promise_) {
return new Promise((resolve, reject) => {
//array.from,会转为真正的数组,将传入的promise实例组成一个真正的狐族
promise_ = Array.from(promise_)
if (promise_.length === 0) {//如果传入的的是一个空数组,会异步的返回一个成功态的promise
resolve([])
} else {
let result = [];//准备一个空数组
let index = 0; //定义一个index
}
for (let i = 0; i < promise_.length; i++) {//循环这几个promise
Promise.resolve(promise_[i]).then(data => {
result[i] = data//给数组增加一项
if(++index===promise_.length){
//如果所有promise都是fulfilled,promise。all实例才变fulfilled
resolve(result)//所有promise都获取完数据,length相等,才会执行他
}
},err=>{
reject(err);
return
})

}
})
}

promise.race源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Promise.ra_ce = function(promises) {
promises = Array.from(promises);
return new Promise((resolve, reject) => {
if(promises.length===0) {
return;
} else {
for(let i=0; i<promises.length; i++) {
Promise.resolve(promises[i]).then(data => {
resolve(data);
return;
}, err => {
reject(err);
return;
})
}
}
})
}

Promise.finally源码

不管成功还是失败,都会走到finally中,并且finally之后,还可以继续then。并且会将值原封不动的传递给后面的then。

1
2
3
4
5
6
7
8
9
10
11
12
13
Promise.prototype.finally_ = function (callback) {
// console.log(this);this指向promise这个实例
return this.then((value) => {//需要返回一个promise实例
return Promise.resolve(callback()).then(() => {
return value;//成功态
});
}, (err) => {//需要返回一个promise实例
return Promise.resolve(callback()).then(() => {
throw err//失败态
})
})
}
Promise.prototype.finally_()

React中的Fragment(性能优化)

React中的Fragment(性能优化)

代替div作为外层,可做不可见的包裹元素

  • 一个组件往往要返回多个元素,同时,react有要求这些元素必须包裹在一个div里
  • 普遍解决办法是用div包裹,这样增加了许多没必要的嵌套,增加了浏览器的渲染压力
  • 解决办法:eract 16 开始,ract支持返回数组
1
2
3
4
5
6
7
8
9
10
11

import React from 'react';

export default function () {
return [
<div>一步 01</div>,
<div>一步 02</div>,
<div>一步 03</div>,
<div>一步 04</div>
];
}
  • 常用办法:Fragments,用来包裹元素作为最外层,减少dom层级
1
2
3
4
5
6
7
8
9
10
11
12
import React from 'react';

export default function () {
return (
<Fragment>
<div>一步 01</div>
<div>一步 02</div>
<div>一步 03</div>
<div>一步 04</div>
</Fragment>
);
}

面试--React中PureComponent原理与核心源码

React中PureComponent原理与核心源码

  • PureComponent 会对 propsstate 进行浅层比较,并减少了跳过必要更新的可能性。
  • React.PureComponent 与 React.Component 很相似。两者的区别在于 React.Component 并未实现 shouldComponentUpdate(),而 React.PureComponent中以浅层对比 prop 和 state 的方式来实现了该函数。
  • ** nextState.items === prevState.items 为 true进行浅层判断,会导致重新render**
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
import React, { Component } from 'react';

function shallowEqual(obj1, obj2) {//传入新老state和prop
if (obj1 === obj2) {//一样直接不改变
return true
}
if (typeof obj1 !== 'object' || obj1 === null || typeof obj2 !== 'object' || obj2 === null) {
return false//检测,如果有一个满足就更新
}
let keys1 = Object.keys(obj1)//拿到所有属性名组成的数组
let keys2 = Object.keys(obj2)//拿到所有属性名组成的数组
if (keys1.length !== keys2.length) {
return false//如果键值对数量不相等,那么更新
}
for (const key of keys1) {//循环进行比较
if (!obj2.hasOwnProperty(key) || obj1[key] !== obj2[key]) {
return false//如果obj2没有key的值,或者表层的key的值不相等时,更新
}
}
return true//走到这里,说明没改变,那么不更新
}

class PureComponent extends Component {//引用shouldcomponentupdate方法
shouldComponentUpdate(nextProps, nextState) {//传入的nextprops,nextState与这次的比较,并且传入
return !shallowEqual(this.props, nextProps) || !shallowEqual(this.state, nextState)
//返回值是ture就更新组件,false就不更新组件,这里调用手写shallowEqual方法
}
render() {
return this.props.children;
}
}

export default PureComponent;

面试—provide与inject用法

##面试—provide与inject用法

爷孙组件传递

不存在双向绑定,用inject注入的
以允许一个祖先组件向其所有子孙后代注入一个依赖,不论组件层次有多深,并在起上下游关系成立的时间里始终生效。

  • 使用场景:由于vue有$parent属性可以让子组件访问父组件。但孙组件想要访问祖先组件就比较困难。通过provide/inject可以轻松实现跨级访问祖先组件的数据
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
//祖宗注入。provide
<template>
<div id="app">
</div>
</template>
<script>
export default {
data () {
return {
datas: [
{
id: 1,
label: '产品一'
},
{
、,
provide {
return {
datas: this.datas
}
}
}
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//后代通过inject都可以拿到
<template>
<div>
<ul>
<li v-for="(item, index) in datas" :key="index">
{{ item.label }}
</li>
</ul>
</div>
</template>
<script>
export default {
inject: ['datas']
}
</script>

session与token

session与token

session 是什么?

session:服务端技术,意为会话控制;
和 cookie 不同,session 是保存在服务器上的,而且并不会随着 http 传递;因为 cookie 保存在客户端,
还是很不安全的,服务器为了杜绝这种事情,在服务器上也搞了一个存储用户信息的东西,这就是 session。
一般用于用户的登录状态,用户 id 等敏感信息的保存;

session 怎么使用?

以登录为例,当我们登录后,服务端会在 cookie 中设置当前用户的登录状态为已经登录,
同时在服务器上生成一份 session 文件,session 中存储用户 id,登录状态的有效期限等;这个 session 文件有
一个 id,生成 session 文件后,服务器还会把 session-id 写进 cookie。然后返回给客户端,此时用户的客户端
收到的 cookie 中包含登录状态和 session-id;等下一次再去请求时,http 协议会自动带着所有的 cookie
去请求;等服务器收到请求后,从 cookie 中把用户信息拿出来,然后再根据 session-id 把 session 读取出来,
再从 session 中查询登录状态,如果登录状态有效,就继续正常的响应,否则就返回登录失效的状态,要求用户登录;

token 原理?

一般在用户登录时,客户端把用户的用户名和密码传递给服务器,服务端进行校验,如果没有问题,就会生成一个
token;token 是一个字符串,这个字符串一般是加密过的,一般包含了用户 id,登录的时间戳,以及 sign
(签名,生成 token 的前几位进行加密的结果,可以防止 token 被篡改,一旦篡改了后面这一段和前面的对不上了)
;生成 token 后,服务器把 token 返回给前端,然后前端下次请求的时候需要带上这个 token,然后服务端在接收
到请求后会首先校验这个 token 是否有效,如果有效继续受理请求,如果无效则直接拒绝;

使用 token 的方式有哪些?

1.服务端也可以直接把 token 写进 cookie 中,下次客户端发起请求时会自动带上;
2.服务端也可以作为数据返给前端,但是前端此时需要手动的保存这个 token,可以保存在 localStorage 中,下次发请求时从 ls 中取出作为参数传给服务器即可
3.服务端返回 token 后,客户端可以把这个 token 写进请求头中,然后服务端每次从请求头中获取;

session 和 cookie 的区别?

session 存在服务器上,是服务器的技术;
cookie 存在客户端,是 http 协议的一部分;